<template>
  <div class="scenes"
  style="
    position: fixed;
    left: 0;
    top: 0;
    z-index: 10;
    pointer-events: none;
    transition: all 1s;
  "
  :style="{
    transform:`translate3d(0,${-index * 100}vh,0)`
  }"
  >
    <div v-for="item in scenes" style="width: 100vw;height: 100vh;">
       <h1 style="padding:100px 50px;font-size: 50px;color: #fff;">
          {{item.text}}
      </h1>
    </div>
  </div>
</template>

<script  setup>
import { onMounted, ref, reactive, toRefs } from 'vue';
import * as THREE from "three"
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"//加载模型库
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"//解压模型库
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader"
import { Water } from "three/examples/jsm/objects/Water2"//导入水纹理
import gsap from "gsap"

//初始化场景
const secene = new THREE.Scene();
//初始化相机
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
//更新摄像机投影矩阵
camera.updateProjectionMatrix()
//设置相机的初始位置
camera.position.set(-3.23, 2.98, 4.06)

//初始化渲染器
const renderer = new THREE.WebGLRenderer({
  //设置抗锯齿
  antialias: true
})
renderer.setSize(window.innerWidth, window.innerHeight)
//将渲染器加入到页面中
document.body.appendChild(renderer.domElement);
//设置色调映射
renderer.outputEncoding = THREE.sRGBEncoding
renderer.toneMapping = THREE.ACESFilmicToneMapping;
//色调映射曝光度
renderer.toneMappingExposure = 0.8
//渲染器允许阴影
renderer.shadowMap.enabled = true
//按照物理渲染
renderer.physicallyCorrectLights = true



//初始化控制器
const controls = new OrbitControls(camera, renderer.domElement)
//设置控制器阻尼效果
controls.enableDamping = true//开启阻尼效果
controls.target.set(-8,2,0)

//初始化加载器loader
const dracoLoader = new DRACOLoader()
dracoLoader.setDecoderPath("./draco/");//设置解压模型的文件
const gltfLoader = new GLTFLoader();
gltfLoader.setDRACOLoader(dracoLoader)//加载解压的模型

//加载模型
gltfLoader.load("./model/scene.glb", (gltf) => {
  const model = gltf.scene;
  console.log("模型", model);
  model.traverse(child => {
    if (child.name === 'Plane') {//隐藏模型中的水面
      child.visible = false
    }
    if (child.isMesh) {//如果当前是物体
      child.castShadow = true;//开启投射阴影
      child.receiveShadow = true;//接收阴影

    }
  })
  secene.add(model)
})
// //添加坐标系
// const axesHelper = new THREE.AxesHelper(4);
// secene.add(axesHelper)

//设置天空环境贴图
const rgbLoader = new RGBELoader()//初始化rgbloder加载器
rgbLoader.load("./textures/sky.hdr", (texture) => {
  texture.mapping = THREE.EquirectangularReflectionMapping//设置纹理为球面映射
  secene.environment = texture//纹理贴图将会被设为场景中所有物理材质的环境贴图
  secene.background = texture//场景背景

})
//设置水面
const waterGeometry = new THREE.CircleGeometry(300, 32)//32边的圆面
const water = new Water(waterGeometry, {
  textureWidth: 1024,
  textureHeight: 1024,
  color: 0xeeeeff,
  flowDirection: new THREE.Vector2(1, 1),
  scale: 100
})
water.rotation.x = - Math.PI / 2
water.position.y = -0.4
secene.add(water)

//添加点光源
const pointLight = new THREE.PointLight(0xffffff, 10)
pointLight.position.set(0.1, 2.4, 0)//设置在房子内中的点光源
pointLight.castShadow = true//开启点光源的投射阴影
secene.add(pointLight)

//添加点光源组
let pointArr = []
let radius = 3;//设置点光源轨迹半径
const pointLightGroup = new THREE.Group()
pointLightGroup.position.set(-8, 2.5, -1.5);//将点光源组设置在树的一旁
for (let i = 0; i < 3; i++) {
  //创建小球当灯泡
  const sphereGeometry = new THREE.SphereGeometry(0.2, 32, 32);//设置几何体
  const sphereMaterial = new THREE.MeshStandardMaterial({//设置标准网格材质
    color: 0xffffff,
    emissive: 0xffffff,//辐射光颜色为纯白色
    emissiveIntensity: 10//辐射光的强度为10

  })
  const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial)//设置网格物体
  sphere.position.set(
    radius * Math.cos(i * (Math.PI * 2 / 3)),
    Math.cos(i * (Math.PI * 2 / 3)),
    radius * Math.sin(i * (Math.PI * 2 / 3)),
  )

  //创建点光源
  const pointLight = new THREE.PointLight(0xffffff, 10)
  sphere.add(pointLight)//网格物体添加点光源
  pointArr.push(sphere)
  pointLightGroup.add(sphere)
}
secene.add(pointLightGroup)//将点光源组添加到场景中

//给点光源组设置补间动画,让灯光小球从0~360度进行旋转
let options = {
  angle: 0
}
gsap.to(options, {
  angle: Math.PI * 2,
  duration: 10,// 动画的持续时间10s
  repeat: -1,//动画的重复次数，这里设置为无限循环。
  ease: "linear",//动画的缓动函数，这里使用了线性缓动
  onUpdate: () => {//动画更新时的回调函数
    pointLightGroup.rotation.y = options.angle
    pointArr.forEach((item, i) => {
      item.position.set(
        radius * Math.cos((i * Math.PI * 2) / 3),
        Math.cos((i * Math.PI * 2) / 3 + options.angle*5),
        radius * Math.sin((i * Math.PI * 2) / 3),
      )
    })

  }
})


//使用补间动画移动相机
let timeLine1=gsap.timeline();
let timeLine2=gsap.timeline();

//定义相机移动函数
function tranlateCamera(position,target){
  timeLine1.to(camera.position,{
    x:position.x,
    y:position.y,
    z:position.z,
    duration:1,//移动过程持续1秒
    ease:"power2.inOut"//采用power2.inOut缓动效果
  })
  timeLine2.to(controls.target,{
    x:target.x,
    y:target.y,
    z:target.z,
    duration:1,
    ease:"power2.inOut"//采用power2.inOut缓动效果
  })

}
//切换相机位置和文字切屏
let scenes=[
   {
      text:"1.看圣诞树",
      callback:()=>{
          //执行函数切换位置
          tranlateCamera(
             new THREE.Vector3(-3.23,3,4.06),
             new THREE.Vector3(-8,2,0)

          )
      }
   },
   {
      text:"2.远处看木屋",
      callback:()=>{
          //执行函数切换位置
          tranlateCamera(
             new THREE.Vector3(7,0,23),
             new THREE.Vector3(0,0,0)
          )
      }
   },
   {
      text:"3.看宝箱",
      callback:()=>{
          //执行函数切换位置
          tranlateCamera(
             new THREE.Vector3(10,3,0),
             new THREE.Vector3(5,2,0)
          )
      }
   },
   {
      text:"4.看星星",
      callback:()=>{
          //执行函数切换位置
          tranlateCamera(
             new THREE.Vector3(8,0,23),
             new THREE.Vector3(0,0,0)
          )
          makeHeart()
      }
   },
   {
      text:"5.看日出",
      callback:()=>{
          //执行函数切换位置
          tranlateCamera(
             new THREE.Vector3(-20,1.3,6.6),
             new THREE.Vector3(5,2,0)
          )
      }
   }
]
//监听鼠标滚轮事件
let index=ref(0)//切换相机的索引值
let flag=false//设置防抖开关
document.addEventListener('wheel',(e)=>{
    console.log("鼠标滚动",e.deltaY);
    if(flag) return
    flag=true
    if(e.deltaY>0){//如果向上滚动
        index.value++
        if(index.value>scenes.length-1){//如果处于数组最后一项，重新设置索引值为0
           index.value=0
           restoreHeart()//当回到初始屏时复原爱心为漫天星效果
        }
    }else{
        index.value--
        if(index.value<0){//如果处于数组第一项，重新设置索引值为数组长度
           index.value=scenes.length-1
        }
    }
    scenes[index.value].callback()
    setTimeout(() => {
      flag=false
    }, 1000);

})


//实例化创建漫天星星
let starsInstance=new THREE.InstancedMesh(//渲染大量具有相同几何体与材质、但具有不同世界变换的物体
   new THREE.SphereGeometry(0.1,32,32),//球形几何体
   new THREE.MeshStandardMaterial({
      color:0xffffff,
      emissive:0xffffff,//辐射光颜色为纯白色
      emissiveIntensity:10////辐射光强调为10
   }),
   100//创建100个这样的网格物体
)
//星星随机到天上
let startArr=[];//开始位置坐标数组
let endArr=[];//结束位置坐标数组
for (let i = 0; i < 100; i++) {
  let x=Math.random() * 100 - 50;
  let y=Math.random() * 100 - 50;
  let z=Math.random() * 100 - 50;
  startArr.push(new THREE.Vector3(x,y,z));

  let matrix=new THREE.Matrix4();
  matrix.setPosition(x,y,z);
  starsInstance.setMatrixAt(i,matrix)//设置实例化化网格的起始随机位置
}
secene.add(starsInstance);

//创建爱心路径
let heartShape=new THREE.Shape()
heartShape.moveTo( 25, 25 );
heartShape.bezierCurveTo( 25, 25, 20, 0, 0, 0 );
heartShape.bezierCurveTo( - 30, 0, - 30, 35, - 30, 35 );
heartShape.bezierCurveTo( - 30, 55, - 10, 77, 25, 95 );
heartShape.bezierCurveTo( 60, 77, 80, 55, 80, 35 );
heartShape.bezierCurveTo( 80, 35, 80, 0, 50, 0 );
heartShape.bezierCurveTo( 35, 0, 25, 25, 25, 25 );

//根据爱心路径获取孔洞点位信息
let center=new THREE.Vector3(0,2,10)//设置爱心的中心位置
for (let i = 0; i < 100; i++) {
    let point=heartShape.getPoint(i/100);
    console.log("爱心路径上的点位信息",point);
    endArr.push(new THREE.Vector3(point.x*0.1+center.x,point.y*0.1+center.y,center.z))
}
//创建爱心动画
function makeHeart(){
   let parms={
     time:0,
   }
   gsap.to(
     parms,{
       time:1,
       duration:1,
       onUpdate:()=>{
          for (let i = 0; i < 100; i++) {
            let x=startArr[i].x + (endArr[i].x - startArr[i].x) * parms.time;
            let y=startArr[i].y + (endArr[i].y - startArr[i].y) * parms.time;
            let z=startArr[i].z + (endArr[i].z - startArr[i].z) * parms.time;
            let matrix=new THREE.Matrix4();//创建矩阵对象
            matrix.setPosition(x,y,z);//设置了该矩阵的位置信息
            starsInstance.setMatrixAt(i,matrix);//StarsInstance的第i个点位上，从而实现了该点位的位置变换
          }
          starsInstance.instanceMatrix.needsUpdate=true//更新实例星星点位的矩阵
       }
     }
   )
}
//复原爱心动画为漫天星效果
function restoreHeart(){
   let parms={
     time:0,
   }
   gsap.to(
     parms,{
       time:1,
       duration:1,
       onUpdate:()=>{
          for (let i = 0; i < 100; i++) {
            let x=endArr[i].x + (startArr[i].x - endArr[i].x) * parms.time;
            let y=endArr[i].y + (startArr[i].y - endArr[i].y) * parms.time;
            let z=endArr[i].z + (startArr[i].z - endArr[i].z) * parms.time;
            let matrix=new THREE.Matrix4();//创建矩阵对象
            matrix.setPosition(x,y,z);//设置了该矩阵的位置信息
            starsInstance.setMatrixAt(i,matrix);//StarsInstance的第i个点位上，从而实现了该点位的位置变换
          }
          starsInstance.instanceMatrix.needsUpdate=true//更新实例星星点位的矩阵
       }
     }
   )
}








//添加平行光
const directLight = new THREE.DirectionalLight(0xffffff, 1);
directLight.position.set(0, 50, 0);
secene.add(directLight)

function render() {//渲染函数
  requestAnimationFrame(render)
  renderer.render(secene, camera)
  controls.update()

}
render()

//当页面发生变化时重新渲染
window.addEventListener("resize", () => {//监听画面变化，更新渲染画面
  console.log("页面发生变化了");
  //更新摄像头
  camera.aspect = window.innerWidth / window.innerHeight
  //更新摄像头的投影矩阵
  camera.updateProjectionMatrix()

  //更新渲染器的渲染范围
  renderer.setSize(window.innerWidth, window.innerHeight)
  //设置渲染器的像素比
  renderer.setPixelRatio(window.devicePixelRatio)
})


</script>
<style >
* {
  padding: 0;
  margin: 0;
}

canvas {
  width: 100vw;
  height: 100vh;
  position: fixed;
  left: 0;
  top: 0;
}
</style>